/**
 * 只有加法和乘法的表达式，两种操作
 * 1 s e: [s, e]区间所表示的表达式的值
 * 2 pos ch: 将pos位置的字符修改为ch
 * 始终保证表达式合法
 * 
 * 显然是一个区间查询单点修改的问题，需要用数据结构维护
 * 如果单独维护数，考虑到优先级，对加法需要用一个二叉树，每个节点下面可能有多个乘法，要套数
 * 每个数字串也可能很长仍然要套数维护，变成了树套树套树，写不了。
 * 
 * 只用线段树维护即可。考虑区间构成，一共有三种可能。
 * 
 * 全数字，标记为SIN；只有乘法，标记为MUL；有加法标记为PLU。
 * 考虑合并操作，因为要进行区间合并所以要记录如下信息：
 * SIN：值与位数
 * MUL：left * mid * right
 * PLU: left * other + mid * other * right 
 * 有了这些信息，很容易完成合并操作。
 * 
 * 还要考虑特殊情况，即SIN恰好是一个操作符，或者MUL与PLU的两端恰好是操作符
 * 全部写在成员函数中。
 * 
 * 线段树本身的类型用多态理论上比较好，但显然不方便。
 * C++也不方便用联合体。最初将三个成员全部放入类型，最后一点MLE了。
 * PLU其实囊括了MUL和SIN，所以线段树类型只包含PLU类型，其他类型在PLU中进行复用即可
 */
#include <bits/stdc++.h>
using namespace std;

using llt = long long;
llt const MOD = 998244353LL;

int Q;
string S;

vector<llt> Pow;

struct SegTree{ // 线段树带延迟

using llt = long long;

enum Type{NUL, SIN, MUL, PLU};

struct mul_t;
struct plu_t;
struct _t;

struct single_t {
    llt val;
    int len;
    single_t(): val(0LL), len(0){}
    single_t(llt a, int b): val(a), len(b){}

    bool isNormal() const {return this->val >= 0LL;}
    bool isSingleMul() const {return -1LL == this->val;}
    bool isSinglePlu() const {return -2LL == this->val;}

    void disp() const {
        cout << "SIN: ";
        dispSimple();
        cout << endl;
    }

    void dispSimple() const {
        if(this->isNormal()){
            cout << "{" << val << ", " << len << "}";
        }else if(this->isSingleMul()){
            cout << " M ";
        }else if(this->isSinglePlu()){
            cout << " P ";
        }
        
    }

    const single_t merge(const single_t & right) const{
        return {(val * Pow[right.len] % MOD + right.val) % MOD, len + right.len};
    }

    static const single_t & singleMul(){
        static const single_t _ (-1LL, 0);
        return _;
    }

    static const single_t & singlePlu(){
        static const single_t _ (-2LL, 0);
        return _;
    }

    const _t normalMergeSingle(const single_t & b) const  {
        if(b.isNormal()){
            return _t(this->merge(b));
        }
        if(b.isSingleMul()){
            return _t(mul_t(*this, singleMul(), 1LL));
        }
        if(b.isSinglePlu()){
            return _t(plu_t(*this, 1LL, singleMul(), 1LL, 0LL));
        }
        assert(0);
        return value_zero();
    }

    const _t normalMergeMul(const mul_t & b) const {
        if(b.left.isNormal()){
            return _t(mul_t(this->merge(b.left), b.right, b.mid));
        }
        if(b.left.isSingleMul()){
            return _t(mul_t(*this, b.right, b.mid));
        }
        assert(0);
        return value_zero();
    }

    const _t normalMergePlu(const plu_t & b) const {
        if(b.mul.left.isNormal()){
            return _t(plu_t(this->merge(b.mul.left), b.other_left, b.mul.right, b.other_right, b.mul.mid));
        }
        if(b.mul.left.isSingleMul()){
            return _t(plu_t(*this, b.other_left, b.mul.right, b.other_right, b.mul.mid));
        }
        if(b.mul.left.isSinglePlu()){
            return _t(plu_t(*this, 1LL, b.mul.right, b.other_right, b.mul.mid));
        }
        assert(0);
        return value_zero();
    }

    const _t specialMergeSingle(const single_t & b) const {
        assert(b.isNormal());
        if(this->isSingleMul()){
            return _t(mul_t(singleMul(), b, 1LL));
        }
        if(this->isSinglePlu()){
            return _t(plu_t(singleMul(), 1LL, b, 1LL, 0LL));
        }
        assert(0);
        return value_zero();
    }

    const _t specialMergeMul(const mul_t & b) const {
        assert(b.left.isNormal());
        if(this->isSingleMul()){
            return _t(mul_t(singleMul(), b.right, b.left.val * b.mid % MOD));
        }
        if(this->isSinglePlu()){ 
            return _t(plu_t(singlePlu(), 1LL, b.right, b.left.val * b.mid % MOD, 0LL));
        }
        assert(0);
        return value_zero();
    }

    const _t specialMergePlu(const plu_t & b) const {
        assert(b.mul.left.isNormal());
        if(this->isSingleMul()){
            return _t(plu_t(singleMul(), b.mul.left.val * b.other_left % MOD, b.mul.right, b.other_right, b.mul.mid));
        }
        if(this->isSinglePlu()){
            return _t(plu_t(singlePlu(), 1LL, b.mul.right, b.other_right, (b.mul.left.val * b.other_right % MOD + b.mul.mid) % MOD));
        }
        assert(0);
        return value_zero();        
    }

    const _t normalMerge(const _t & b) const {
        assert(this->isNormal());
        switch(b.type){
            case SIN: return this->normalMergeSingle(b.plu.mul.left);
            case MUL: return this->normalMergeMul(b.plu.mul);
            case PLU: return this->normalMergePlu(b.plu);
        }
        assert(0);
        return value_zero();
    }

    const _t specialMerge(const _t & b) const {
        switch(b.type){
            case SIN: return this->specialMergeSingle(b.plu.mul.left);
            case MUL: return this->specialMergeMul(b.plu.mul);
            case PLU: return this->specialMergePlu(b.plu);
        }
        assert(0);
        return value_zero();
    }
};

struct mul_t{
    single_t left;
    single_t right;
    llt mid;
    llt total_value;
    mul_t(): left(), right(), mid(0LL), total_value(0LL){}
    mul_t(const single_t & a, const single_t & b, llt c):left(a),right(b),mid(c),total_value(a.val * b.val % MOD * c % MOD){}
   
    void disp()const{
        cout << "MUL: ";
        left.dispSimple();
        cout << ", " << mid << ", ";
        right.dispSimple();
        cout << endl;
    }   

    const _t normalMergeSingle(const single_t & b) const  {
        if(b.isNormal()){
            return _t(mul_t(this->left, this->right.merge(b), this->mid));
        }
        if(b.isSingleMul()){
            return _t(mul_t(this->left, single_t::singleMul(), this->mid * this->right.val % MOD)); 
        }
        if(b.isSinglePlu()){
            return _t(plu_t(this->left, this->mid * this->right.val % MOD, single_t::singleMul(), 1LL, 0LL));
        }
        assert(0);
        return value_zero();        
    }

    const _t normalMergeMul(const mul_t & b) const {
        if(b.left.isNormal()){
            return _t(mul_t(this->left, b.right, this->mid * this->right.merge(b.left).val % MOD * b.mid % MOD));   
        }
        if(b.left.isSingleMul()){
            return _t(mul_t(this->left, b.right, this->mid * this->right.val % MOD * b.mid % MOD));
        }
        assert(0);
        return value_zero();        
    }    

    const _t normalMergePlu(const plu_t & b) const {
        if(b.mul.left.isNormal()){
            return _t(plu_t(this->left, this->mid * this->right.merge(b.mul.left).val % MOD * b.other_left % MOD, b.mul.right, b.other_right, b.mul.mid));
        }
        if(b.mul.left.isSingleMul()){
            return _t(plu_t(this->left,this->mid * this->right.val % MOD * b.other_left % MOD, b.mul.right, b.other_right, b.mul.mid));
        }
        if(b.mul.left.isSinglePlu()){
            return _t(plu_t(this->left, this->mid * this->right.val % MOD, b.mul.right, b.other_right, b.mul.mid));
        }
        assert(0);
        return value_zero();        

    }

    const _t specialMergeSingle(const single_t & b) const  {
        assert(b.isNormal());
        if(this->right.isSingleMul()){
            return _t(mul_t(this->left, b, this->mid));
        }
        assert(0);
        return value_zero();
    }

    const _t specialMergeMul(const mul_t & b) const {
        assert(b.left.isNormal());
        if(this->right.isSingleMul()){
            return _t(mul_t(this->left, b.right, this->mid * b.left.val % MOD * b.mid % MOD));            
        }
        assert(0);
        return value_zero();        
    }

    const _t specialMergePlu(const plu_t & b) const {
        assert(b.mul.left.isNormal());
        if(this->right.isSingleMul()){
            return _t(plu_t(this->left, this->mid * b.mul.left.val % MOD * b.other_left % MOD, b.mul.right, b.other_right, b.mul.mid));            
        }

        assert(0);
        return value_zero();        
    }

    const _t normalMerge(const _t & b) const {
        assert(this->right.isNormal());
        switch(b.type){
            case SIN: return this->normalMergeSingle(b.plu.mul.left);
            case MUL: return this->normalMergeMul(b.plu.mul);
            case PLU: return this->normalMergePlu(b.plu);
        }
        assert(0);
        return value_zero();
    }

    const _t specialMerge(const _t & b) const {
        switch(b.type){
            case SIN: return this->specialMergeSingle(b.plu.mul.left);
            case MUL: return this->specialMergeMul(b.plu.mul);
            case PLU: return this->specialMergePlu(b.plu);
        }
        assert(0);
        return value_zero();
    }    
};

struct plu_t{
    mul_t mul;
    llt other_left;
    llt other_right;
    llt total_value;
    plu_t():mul(),other_left(0),other_right(0),total_value(0){}
    plu_t(const single_t & a, llt b, const single_t & c, llt d, llt e)
    :mul(a, c, e),other_left(b),other_right(d)
    ,total_value((a.val * b % MOD + c.val * d % MOD + e) % MOD){}

    void disp()const{
        cout << "PLU: ";
        mul.left.dispSimple();
        cout << " * " << other_left << " + " << mul.mid << " + " << other_right << " * ";
        mul.right.dispSimple();
        cout << endl; 
    }

    const _t normalMergeSingle(const single_t & b) const  {
        if(b.isNormal()){
            return _t(plu_t(this->mul.left, this->other_left, this->mul.right.merge(b), this->other_right, this->mul.mid));
        }
        if(b.isSingleMul()){
            return _t(plu_t(this->mul.left, this->other_left, single_t::singleMul(), this->other_right * this->mul.right.val % MOD, this->mul.mid));
        }
        if(b.isSinglePlu()){
            return _t(plu_t(this->mul.left, this->other_left, single_t::singlePlu(), 1LL, (this->mul.mid + this->other_right * this->mul.right.val % MOD) % MOD));
        }
        assert(0);
        return value_zero();
    }

    const _t normalMergeMul(const mul_t & b) const {
        if(b.left.isNormal()){
            return _t(plu_t(this->mul.left, this->other_left, b.right, this->other_right * this->mul.right.merge(b.left).val % MOD * b.mid % MOD, this->mul.mid));
        }
        if(b.left.isSingleMul()){
            return _t(plu_t(this->mul.left, this->other_left, b.right, this->other_right * this->mul.right.val % MOD * b.mid % MOD, this->mul.mid));
        }
        assert(0);
        return value_zero();
    }

    const _t normalMergePlu(const plu_t & b) const {
        if(b.mul.left.isNormal()){
            return _t(plu_t(this->mul.left, this->other_left, b.mul.right, b.other_right, (this->mul.mid + this->other_right * this->mul.right.merge(b.mul.left).val % MOD * b.other_left % MOD + b.mul.mid) % MOD));
        }
        if(b.mul.left.isSingleMul()){
            return _t(plu_t(
                this->mul.left, 
                this->other_left, 
                b.mul.right, 
                b.other_right, 
                (this->mul.mid + this->other_right * this->mul.right.val % MOD * b.other_left % MOD + b.mul.mid) % MOD
            ));
        }

        assert(0);
        return value_zero();
    }

    const _t specialMergeSingle(const single_t & b) const  {
        assert(b.isNormal());
        if(this->mul.right.isSingleMul()){
            return _t(plu_t(
                this->mul.left, 
                this->other_left, 
                b, 
                this->other_right, 
                this->mul.mid
            ));
        }
        if(this->mul.right.isSinglePlu()){
            return _t(plu_t(this->mul.left, this->other_left, b, 1LL, this->mul.mid));
        }
        assert(0);
        return value_zero();
    }

    const _t specialMergeMul(const mul_t & b) const {
        assert(b.left.isNormal());
        if(this->mul.right.isSingleMul()){
            return _t(plu_t(this->mul.left, this->other_left, b.right, this->other_right * b.left.val % MOD * b.mid % MOD, this->mul.mid));
        }
        if(this->mul.right.isSinglePlu()){
            return _t(plu_t(this->mul.left, this->other_left, b.right, b.left.val * b.mid % MOD, this->mul.mid));
        }
        assert(0);
        return value_zero();
    }

    const _t specialMergePlu(const plu_t & b) const {
        assert(b.mul.left.isNormal());
        if(this->mul.right.isSingleMul()){
            return _t(plu_t(this->mul.left, this->other_left, b.mul.right, b.other_right, (this->mul.mid + this->other_right * b.mul.left.val % MOD * b.other_left % MOD + b.mul.mid) % MOD));
        }
        if(this->mul.right.isSinglePlu()){
            return _t(plu_t(this->mul.left, this->other_left, b.mul.right, b.other_right, (this->mul.mid + b.mul.left.val * b.other_left % MOD + b.mul.mid) % MOD));
        }
        assert(0);
        return value_zero();        
    }

    const _t normalMerge(const _t & b) const {
        assert(this->mul.right.isNormal());
        switch(b.type){
            case SIN: return this->normalMergeSingle(b.plu.mul.left);
            case MUL: return this->normalMergeMul(b.plu.mul);
            case PLU: return this->normalMergePlu(b.plu);
        }
        assert(0);
        return value_zero();
    }

    const _t specialMerge(const _t & b) const {
        switch(b.type){
            case SIN: return this->specialMergeSingle(b.plu.mul.left);
            case MUL: return this->specialMergeMul(b.plu.mul);
            case PLU: return this->specialMergePlu(b.plu);
        }
        assert(0);
        return value_zero();
    }
};

struct _t{
    Type type;
    plu_t plu;

    _t():type(NUL){}
    _t(const single_t & a):type(SIN){plu.mul.left = a;}
    _t(const mul_t & a):type(MUL){plu.mul = a;}
    _t(const plu_t & a):type(PLU),plu(a){}


    void disp(int t, int s, int e) const {
        cout << "Node " << t << ":" << endl;
        cout << "[" << s << ", " << e << "]: " << S.substr(s, e - s + 1) << endl;

        switch(type){
            case SIN:{
                plu.mul.left.disp();
                break;
            }
            case MUL:{
                plu.mul.disp();
                break;
            }
            case PLU:{
                plu.disp();
                break;
            }
            default:assert(0);
        }

        return;
    }
};

int N;


using value_type = _t;
vector<value_type> data; // 线段树

using lazy_type = llt;
vector<lazy_type> lazy; // 延迟标记

/// 从下往上计算信息，要变动
value_type _up_(const value_type & ls, const value_type & rs) {
    if(ls.type == NUL) return rs;
    if(rs.type == NUL) return ls;

    switch(ls.type){
        case SIN:{
            if(ls.plu.mul.left.isNormal()) return ls.plu.mul.left.normalMerge(rs);
            return ls.plu.mul.left.specialMerge(rs);
        }
        case MUL:{
            if(ls.plu.mul.right.isNormal()) return ls.plu.mul.normalMerge(rs);
            return ls.plu.mul.specialMerge(rs);
        }
        case PLU:{
            if(ls.plu.mul.right.isNormal()) return ls.plu.normalMerge(rs);
            return ls.plu.specialMerge(rs);
        }
    }

    assert(0);
    return value_zero();
}

/// 初始化，清零，不用动
void init(int n) {
    data.assign((N = n) + 1 << 2, value_zero());
}

/// 这个函数不用动
void modify(int pos, char delta){
    _modify(1, 1, N, pos, delta);
}

/// 这个函数不用动
value_type query(int a, int b){
    return _query(1, 1, N, a, b);
}

/// 这个函数不用动
void build() {
    _build(1, 1, N);
}

/// 几乎不用动
value_type _query(int t, int s, int e, int a, int b) {
    if(a <= s and e <= b) {
        return data[t];
    }

    int mid = (s + e) >> 1;
    value_type ans = value_zero(); // 如果求最值，这里不能用zero
    if(a <= mid) ans = _up_(ans, _query(lson(t), s, mid, a, b));
    if(mid < b) ans = _up_(ans, _query(rson(t), mid + 1, e, a, b));
    return ans;
}

/// 几乎不用动
void _modify(int t, int s, int e, int pos, char delta) {
    if(s == e) {
        data[t] = mkValue(delta);
        return;
    }

    int mid = (s + e) >> 1;
    if(pos <= mid) _modify(lson(t), s, mid, pos, delta);
    else _modify(rson(t), mid + 1, e, pos, delta);
    _pushUp(t);
    return;
}

/// 这个函数不用动
void _pushUp(int t) {
    data[t] = _up_(data[lson(t)], data[rson(t)]);
}

/// 几乎不用动
void _build(int t, int s, int e) {
    if(s == e) {
        data[t] = mkValue(S[s]); // 注意提供value_type的初始化
        return; 
    }
    int mid = (s + e) >> 1;
    _build(lson(t), s, mid);
    _build(rson(t), mid + 1, e);
    _pushUp(t);
}

/// 辅助函数，视线段树信息类型而变动
static const value_type & value_zero() {
    static const value_type VALUE0;
    return VALUE0;
}

static const _t mkValue(char ch){
    if(isdigit(ch)){
        return _t(single_t((llt)(ch - '0'), 1));
    }
    if('*' == ch){
        return _t(single_t(-1LL, 0));
    }
    if('+' == ch){
        return _t(single_t(-2LL, 0));
    }
    throw runtime_error("wrong char.");
}

/// 这两个函数不用变动
static int lson(int x) {return x << 1;}
static int rson(int x) {return lson(x) | 1;}

void disp() const {
    _disp(1, 1, N);
}

void _disp(int t, int s, int e) const {
    data[t].disp(t, s, e);
    if(s != e){
        int mid = (s + e) >> 1;
        _disp(lson(t), s, mid);
        _disp(rson(t), mid + 1, e);
    }
}

};


SegTree St;

void work(){ 
    cin >> Q >> S;   
    int n = S.length();

    Pow.assign(n + 1, 0);
    Pow[0] = 1LL;
    for(int i=1;i<=n;++i) Pow[i] = Pow[i - 1] * 10LL % MOD;

    S = " " + S;
    St.init(n);
    St.build();

#ifndef ONLINE_JUDGE
    // cout << "Init: " << endl;
    // St.disp();
#endif

    for(int cmd,q=1;q<=Q;++q){
        cin >> cmd;
        if(1 == cmd){
            int a, b; cin >> a >> b;
            auto ans = St.query(a, b);
            if(SegTree::SIN == ans.type) cout << ans.plu.mul.left.val << "\n";
            else if(SegTree::MUL == ans.type) cout << ans.plu.mul.total_value << "\n";
            else if(SegTree::PLU == ans.type) cout << ans.plu.total_value << "\n";
            else throw runtime_error("the answer of query has wrong type.");
        }else if(2 == cmd){  
            int pos; char ch; cin >> pos >> ch;
            S[pos] = ch;
            St.modify(pos, ch);

#ifndef ONLINE_JUDGE
            // cout << "Modify " << q << ":" << endl;
            // St.disp();
#endif

        }else{
            throw runtime_error("wrong cmd");
        }
    }

	return;
}

int main(){
#ifndef ONLINE_JUDGE
    freopen("z.txt", "r", stdin);
#endif
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);	
    int nofkase = 1;
	// cin >> nofkase;
	while(nofkase--) work();
	return 0;
}